#include "common.h"
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *ip;
char *port;
struct MagicPck pck_cache[2] = {0};
int cache_count = 0;
int main()
{
    ip = getenv("serverip");
    port = getenv("serverport");
    // ip = SERVER;
    // port = SERVER_PORT;
    SLOG("udp sock on %s:%s", ip, port);
    getchar();
    int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s == -1)
    {
        goto err;
    }
    struct sockaddr_in local = {
        .sin_family = AF_INET,
        .sin_port = htons(atoi(port)),
        .sin_addr = inet_addr(ip)};
    if (bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_in)) < 0)
    {
        goto err;
    };

    SET_NONBLOCK(s);

    start(s);
err:
    ELOG("%s", strerror(errno));
}

void start(int fd)
{
    safe_send_to(fd, NULL);
}
struct MagicPck *safe_recv_from(int fd)
{
    int m_siz = sizeof(struct MagicPck);
    struct MagicPck *m_ptr = malloc(m_siz);
    if (m_ptr == NULL)
    {
        goto err;
    }
    int s_siz = sizeof(struct sockaddr);
    struct sockaddr *s_ptr = malloc(s_siz);
    if (s_ptr == NULL)
    {
        free(m_ptr);
        goto err;
    }
    int e = recvfrom(fd, m_ptr, m_siz, 0, s_ptr, &s_siz);
    if (e == -1)
    {
        free(m_ptr);
        free(s_ptr);
        goto err;
    }
    ADDRLOG(s_ptr,"nat gateway");
    ADDRLOG(&(m_ptr->addr),"magic pck");

    struct sockaddr_in peer={
        .sin_family=AF_INET,
        .sin_port = ntohs(atoi(CLIENT_PORT)),
        .sin_addr = inet_addr(CLIENT),
    };
    struct sockaddr_in peer2={
        .sin_family=AF_INET,
        .sin_port = ntohs(atoi(CLIENT2_PORT)),
        .sin_addr = inet_addr(CLIENT2),
    };
    if (memcmp(&(m_ptr->addr), &peer, sizeof(struct sockaddr)) == 0 || memcmp(&(m_ptr->addr), &peer2, sizeof(struct sockaddr)) == 0)
    {
        memcpy(&(m_ptr->addr),s_ptr,sizeof(struct sockaddr));
        free(s_ptr);
        return m_ptr;
    }
    free(m_ptr);
    free(s_ptr);
    return NULL;
err:
    ELOG("%s", strerror(errno));
    return NULL;
}

int safe_send_to(int fd, const struct sockaddr *t)
{

    while (cache_count != 2)
    {
        usleep(300000);
        struct MagicPck *pck = gen_magic_pck(fd);
        if (pck == NULL)
        {
            continue;
        }
   
        // int cmp_flag = memcmp(&(pck_cache[cache_count-1]), pck, sizeof(pck));
        // SLOG("%s %d %s %d","memcmp",cmp_flag, "cache_count",cache_count);

        if (cache_count > 0 && memcmp(&(pck_cache[cache_count-1]), pck, sizeof(struct MagicPck)) == 0)
        {
            free(pck);
            continue;
        }
        // pck_cache[cache_count] = *pck;
        memcpy(&pck_cache[cache_count],pck,sizeof(struct MagicPck));
        PRINT_PTR(pck,struct MagicPck,"pck cmp1");
        PRINT_PTR(&(pck_cache[cache_count]),struct MagicPck,"pck cmp2");
        cache_count++;
        for (int i = 0; i < cache_count; i++)
        {
            ADDRLOG(&(pck_cache[i].addr),"cache");
        }
        
        free(pck);
    }
    while (1)
    {
        usleep(300000);
        struct sockaddr *peer1 = gen_peer(&(pck_cache[0]));
        if (peer1 == NULL)
        {
            continue;
            ;
        }
        struct sockaddr *peer2 = gen_peer(&(pck_cache[1]));
        if (peer2 == NULL)
        {
            free(peer1);
            continue;
        }
        struct MagicPck peer_1_mgk = {
            .magic = SERVER_SEND,
        };
        struct MagicPck peer_2_mgk = {
            .magic = SERVER_SEND,
        };

        memcpy(&(peer_1_mgk.addr), peer1, sizeof(struct sockaddr));
        memcpy(&(peer_2_mgk.addr), peer2, sizeof(struct sockaddr));
        int e1 = sendto(fd, &peer_1_mgk, sizeof(struct MagicPck), 0, peer2, sizeof(struct sockaddr));
        int e2 = sendto(fd, &peer_2_mgk, sizeof(struct MagicPck), 0, peer1, sizeof(struct sockaddr));
        free(peer1);
        free(peer2);
    }
    return 0;
}

struct sockaddr *gen_peer(const struct MagicPck *pck)
{
    if (pck == NULL)
    {
        return NULL;
    }
    struct sockaddr *addr = malloc(sizeof(struct sockaddr));
    if (addr == NULL)
    {
        goto err;
    }

    memcpy(addr, &(pck->addr), sizeof(struct sockaddr));
    return addr;

err:
    ELOG("%s", strerror(errno));
    return NULL;
}

struct MagicPck *gen_magic_pck(int fd)
{
    struct MagicPck *pck = safe_recv_from(fd);
    if (pck == NULL)
    {
        goto err;
    }
    SLOG("pck->magic %d",pck->magic);
    if (pck->magic == PEER_SEND)
    {
        log_magic_pck(pck);
        return pck;
    }
    else
    {
        free(pck);
        return NULL;
    }
err:
    ELOG("%s", strerror(errno));
    return NULL;
}

void log_magic_pck(const struct MagicPck *pck)
{
    struct sockaddr_in *p = (struct sockaddr_in *)&(pck->addr);
    SLOG("%s:%d", inet_ntoa(p->sin_addr), ntohs(p->sin_port));
}
